/*
 * Copyright (C) 2012 Damien Girard <dgirard@nativesoft.fr>
 * Copyright (C) 2012 Sebastien Baudrand <sebbaudrand@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package fr.umlv.qroxy.rules;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;


/**
 * Parse a configuration file and create the rules
 * 
 * @author Damien Girard <dgirard@nativesoft.fr>
 * @author Sebastien BAUDRAND <sebbaudrand@gmail.com>
 */
public class Rule {

    private final double minimalSpeed;
    private final double maximalSpeed;
    private final int weight;
    private final int maxCacheSize;
    private final Pattern urlPattern;
    private final HashMap<String, Pattern> httpHeaders;
    private final String nameRule;
    private final boolean forbidenRequest;
    private final boolean privateData;

    /**
     * Create an object Rule. This object definite a rule and values who can be
     * used by the proxy.
     *
     * @param minimalSpeed
     * @param maximalSpeed
     * @param weight
     * @param urlPattern
     * @param htttpHeader
     * @param forbidenRequest
     */
    private Rule(double minimalSpeed, double maximalSpeed, int weight,
            Pattern urlPattern, HashMap<String, Pattern> htttpHeader,
            boolean forbidenRequest, String nameRule, int maxCacheSize, Boolean privateData) {
        this.minimalSpeed = minimalSpeed;
        this.maximalSpeed = maximalSpeed;
        this.weight = weight;
        this.urlPattern = urlPattern;
        this.httpHeaders = htttpHeader;
        this.forbidenRequest = forbidenRequest;
        this.nameRule = nameRule;
        this.maxCacheSize = maxCacheSize;
        this.privateData = privateData;
    }

    /**
     * Parses a configuration file to retrieve the rules to apply. Create all
     * rules contains in the configuration file. If you change the configuration
     * file when qroxy is started, you can restart qroxy.
     *
     * @param file
     */
    public static ArrayList<Rule> createRulesFromFile(File file) throws Exception {
        double minSpeed = -1;
        double maxSpeed = -1;
        int weightRule = -1;
        int maxCSize = -1;
        Pattern urlPat = null;
        HashMap<String, Pattern> httpHead = new HashMap<String, Pattern>();
        String nRule = null;
        boolean fRequest = true;
        boolean privateD = false;
        boolean privateC = false;
        FileInputStream ips = null;
        int lineCounter = 0;
        ArrayList<Rule> rules = new ArrayList<Rule>();
        try {
            // Open file
            ips = new FileInputStream(file);
            InputStreamReader reader = new InputStreamReader(ips);
            BufferedReader br = new BufferedReader(reader);
            Scanner sc = new Scanner(br);
            // Parse file
            while (true) {
                lineCounter++;
                String line = sc.nextLine();
                // All line who start with # or contains # are considered since a comment
                if (!line.contains("#")) {
                    if (line.contains("[") && !line.contains("#")) {
                        // Add the new rules if it is a valide rules
                        if ((minSpeed != -1 || maxSpeed != -1 || weightRule != -1)
                                && (urlPat != null || httpHead != null) && nRule != null) {
                            rules.add(new Rule(minSpeed, maxSpeed, weightRule, urlPat, httpHead, fRequest, nRule, maxCSize, privateD));
                            minSpeed = -1;
                            maxSpeed = -1;
                            weightRule = -1;
                            maxCSize = -1;
                            urlPat = null;
                            httpHead = new HashMap<String, Pattern>();
                            nRule = null;
                            fRequest = true;
                            privateC = false;
                            privateD = false;
                        }
                        // Check if the rules is valid
                        if (rules.size() >= 2) {
                            if (!validateRules(rules)) {

                                throw new Exception("Configuration File parsing. Error in block " + nRule);
                            }
                        }
                        // The name of a rules manage the position in the cache 
                        String s = line.substring(1, line.length() - 1);
                        if (s == null) {
                            throw new Exception("Parsing error. A string is expected. Error at line " + lineCounter);
                        }
                        nRule = s;
                    } //parse all the line to have the good value
                    else {

                        if (line.contains("url") && !line.contains("#")) {
                            String[] t = line.split("=");
                            urlPat = Pattern.compile(t[1]);
                        } else if (line.contains("weight") && !line.contains("#")) {
                            String[] t = line.split("=");
                            weightRule = Integer.valueOf(t[1]);
                        } else if (line.contains("minRate") && !line.contains("#")) {
                            String[] t = line.split("=");
                            minSpeed = Double.valueOf(t[1]);
                        } else if (line.contains("maxRate") && !line.contains("#")) {
                            String[] t = line.split("=");
                            maxSpeed = Double.valueOf(t[1]);
                        } else if (line.contains("http.") && !line.contains("#")) {
                            String[] t = line.split("=");
                            httpHead.put(t[0], Pattern.compile(t[1]));
                        } else if (line.contains("maxCacheSize") && !line.contains("#")) {
                            String[] t = line.split("=");
                            maxCSize = Integer.valueOf(t[1]);
                        } else if (line.contains("privateData") && !line.contains("#")) {
                            String[] t = line.split("=");
                            privateD = Boolean.valueOf(t[1]);
                        }
                    }
                }
                
                if (!sc.hasNextLine()) {
                    if ((minSpeed != -1 || maxSpeed != -1 || weightRule != -1)
                            && (urlPat != null || httpHead != null) && nRule != null) {
                        rules.add(new Rule(minSpeed, maxSpeed, weightRule, urlPat, httpHead, fRequest, nRule, maxCSize, privateD));
                    }
                    break;
                }
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(RuleChecker.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                ips.close();
            } catch (IOException ex) {
                Logger.getLogger(RuleChecker.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return rules;
    }

    /**
     * Validates an http header. Return true if it is valide, else return false.
     *
     * @param header
     * @return true if the rule match the header http
     */
    public boolean validate(HeaderHttp header) {
        if (header.getUrlToCall().equals(urlPattern)) {
            return true;
        }
        Iterator it = header.getProperties().keySet().iterator();
        while (it.hasNext()) {
            if (header.getProperties().values().equals(httpHeaders.values())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if a rules is correct
     *
     * @param index
     * @return true if the minimum values is informed
     */
    private static Boolean validateRules(ArrayList<Rule> rules) {
        Rule rule = rules.get(rules.size() - 2);
        if (rule.getMaximalSpeed() == 0 && rule.getMinimalSpeed() == 0 && rule.getWeight() == 0) {
            return false;
        }
        if (rule.getHttpHeaders().isEmpty() && rule.getUrlPattern() == null) {
            return false;
        }
        return true;
    }

    /**
     * Get the maximal speed of the current rules.
     *
     * @return double the maximal rate
     */
    public double getMaximalSpeed() {
        return this.maximalSpeed;
    }

    /**
     * Get the minimal speed of the current rules.
     *
     * @return double the minnimal rate
     */
    public double getMinimalSpeed() {
        return this.minimalSpeed;
    }

    /**
     * Get the weight of the current rules. The wheight is priority number of a
     * rules.
     *
     * @return int the weight
     */
    public int getWeight() {
        return this.weight;
    }

    /**
     * Get the maximal cache size of the current rules. The wheight is priority
     * number of a rules.
     *
     * @return int
     */
    public Integer getMaxCacheSize() {
        return maxCacheSize;
    }

    /**
     * Get rule name of the current rules. The wheight is priority number of a
     * rules.
     *
     * @return int the weight
     */
    public String getNameRule() {
        return nameRule;
    }

    /**
     * Get the url of the rule
     *
     * @return Pattern url
     */
    public Pattern getUrlPattern() {
        return this.urlPattern;
    }

    /**
     * Get the properties of the rule
     *
     * @return HashMap<String, Pattern> key is the command, value is the pattern
     */
    public HashMap<String, Pattern> getHttpHeaders() {
        return this.httpHeaders;
    }

    /**
     * Get if a reuest is valide or not.
     *
     * @return true if the request is valid
     */
    public boolean getForbidenRequest() {
        return this.forbidenRequest;
    }

    /**
     *
     *
     * @return
     */
    public boolean getPrivateData() {
        return privateData;
    }

    @Override
    public String toString() {
        return nameRule + " " + urlPattern + " " + httpHeaders + " " + maximalSpeed + " " + minimalSpeed + " " + maxCacheSize + " " + weight + " " + privateData + " " + forbidenRequest;
    }
}